import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;

/**
 * @Author: tjc
 * @description
 * @Date Created in 14:41 2021-01-08
 */
public class Main {
    public static void main(String[] args) {
        // 确保一个漂亮的外观风格
        JFrame.setDefaultLookAndFeelDecorated(true);

        EventQueue.invokeLater(() -> {
            JFrame client = new GameClient();
            client.add(new Control());
            client.setVisible(true);
        });
    }


}

class Constant {
    public static final int FIELD_START_COL = 100;
    public static final int FIELD_START_ROW = 100;
    public static int WINDOWS_WIDTH = 1000;
    public static int WINDOWS_LENTH = 1000;

    public static int CELL_LEN = 30;


    public static int FIELD_WIDTH = 10;
    public static int FIELD_LEN = 20;

}

/**
 * 运行窗口
 */
class GameClient extends JFrame {
    public GameClient() {
        setSize(Constant.WINDOWS_WIDTH, Constant.WINDOWS_LENTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setBackground(Color.GRAY);
        setTitle("俄罗斯方块");
    }
}

class Control extends JComponent implements KeyListener {

    private int[][] playField = new int[Constant.FIELD_LEN][Constant.FIELD_WIDTH];

    private Block ctlBlock = null;

    private int topLine = 0;

    public Control() {
        setFocusable(true);
        addKeyListener(this);

        ctlBlock = createBlock();
        new PaintThread().start();
        new FallThread().start();
    }


    private class FallThread extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (!touchBottom(ctlBlock)) {
                    ctlBlock.fall();
                } else {
                    //到底部后创建一个新方块

                    addBlock(ctlBlock);
                    clearRow(ctlBlock);
                    ctlBlock = createBlock();
                }
            }
        }
    }

    private boolean touchBottom(Block block) {
        for (int i = 0; i < block.getBody().size(); i++) {
            Cell cell = block.getBody().get(i);
            if (cell.getRow() >= Constant.FIELD_LEN - 1) {
                return true;
            }
            if (playField[cell.getRow()+1][cell.getCol()] == 1) {
                return true;
            }
        }
        return false;
    }

    private void addBlock(Block block) {
        block.getBody().stream().forEach(cell -> {
            playField[cell.getRow()][cell.getCol()] = 1;
        });
    }

    private Block createBlock() {
        Random random = new Random();

        return BlockFactory.createBlock(random.nextInt(2));
    }

    /**
     * 刷新图像线程
     */
    private class PaintThread extends Thread {

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                repaint();
            }
        }
    }



    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        for (int i = 0; i < Constant.FIELD_LEN; i++) {
            int length = Constant.FIELD_WIDTH;
            for (int j = 0; j < length; j++) {
                if (playField[i][j] == 1) {
                    g2d.setColor(Color.WHITE);//设置画图的颜色
                } else if (playField[i][j] == 0) {
                    g2d.setColor(Color.BLACK);//设置画图的颜色
                }
                g2d.fill3DRect( Constant.FIELD_START_ROW + j * Constant.CELL_LEN,Constant.FIELD_START_COL + i * Constant.CELL_LEN, Constant.CELL_LEN, Constant.CELL_LEN, true);//填充一个矩形
            }
        }
        if (ctlBlock != null) {
            drawBlock(g, ctlBlock);
        }
    }

    /**
     * 功能描述: 清除已经填充满的行
     *
     * @Author: tjc
     * @Date: 2021-01-11 9:40
     */
    private void clearRow(Block block) {
        //todo 可优化，只检测变动的行
        block.getBody().stream().forEach(cell -> {
            int[] row = playField[cell.getRow()];
            boolean full = true;
            for (int i = 0; i < row.length; i++) {
                if(row[i] == 0){
                    full = false;
                    break;
                }
            }
            if(full) {
                System.out.println("full row:" + cell.getRow());
                for (int i = 0; i < row.length; i++) {
                    row[i] = 0;
                }
            }
        });

    }

    private void drawBlock(Graphics g, Block block) {
        Graphics2D g2d = (Graphics2D) g;

        block.getBody().stream().forEach(cell -> {
            g2d.setColor(Color.WHITE);//设置画图的颜色
            g2d.fill3DRect(Constant.FIELD_START_ROW + cell.getCol() * Constant.CELL_LEN, Constant.FIELD_START_COL + cell.getRow() * Constant.CELL_LEN, Constant.CELL_LEN, Constant.CELL_LEN, true);//填充一个矩形
        });

    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {


        if (e.getKeyCode() == KeyEvent.VK_W) {
            ctlBlock.reverseRotate();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            if(ctlBlock.leftBorder() > 0)
            {
                ctlBlock.leftMove();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            if(ctlBlock.rightBorder() < Constant.FIELD_WIDTH - 1) {
                ctlBlock.rightMove();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            if(!touchBottom(ctlBlock))
            {
                ctlBlock.downMove();
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }


}


abstract class Block {

    public static int TYPE_I = 0;
    public static int TYPE_L = 1;
    public static int TYPE_S = 2;
    public static int TYPE_Z = 3;

    protected List<Cell> body;
    protected Cell center;
    private int bootomLine;


    public List<Cell> getBody() {
        return body;
    }

    public void setBody(List<Cell> body) {
        this.body = body;
    }

    public String print() {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < this.body.size(); i++) {
            str.append(this.body.get(i).print());
        }
        return '[' + str.toString() + ']';
    }

    public static int absCol(Cell cell1, Cell cell2) {
        return Math.abs(cell1.getCol() - cell2.getCol());
    }

    public static int absRow(Cell cell1, Cell cell2) {
        return Math.abs(cell1.getRow() - cell2.getRow());
    }

    //逆时针旋转
    public boolean reverseRotate() {
        body.stream().forEach(cell -> {
            int newRow, newCol;
            if ((cell.getCol() >= center.getCol())) {
                newRow = center.getRow() - absCol(center, cell);
            } else {
                newRow = center.getRow() + absCol(center, cell);
            }
            if (cell.getRow() >= center.getRow()) {
                newCol = center.getCol() + absRow(center, cell);
            } else {
                newCol = center.getCol() - absRow(center, cell);
            }
            cell.setRow(newRow);
            cell.setCol(newCol);
        });
        return true;
    }

    //顺时针旋转
    public boolean rotate() {
        body.stream().forEach(cell -> {
            int newRow, newCol;
            if ((cell.getCol() >= center.getCol())) {
                newRow = center.getRow() + absCol(center, cell);
            } else {
                newRow = center.getRow() - absCol(center, cell);
            }
            if (cell.getRow() >= center.getRow()) {
                newCol = center.getCol() - absRow(center, cell);
            } else {
                newCol = center.getCol() + absRow(center, cell);
            }
            cell.setRow(newRow);
            cell.setCol(newCol);
        });
        return true;
    }

    public int leftBorder() {
        final Optional<Cell> cell = this.body.stream().max((a, b) -> {
            if (a.getCol() < b.getCol()) {
                return 1;
            } else return -1;
        });
        return cell.get().getCol();
    }

    public int rightBorder() {
        final Optional<Cell> cell = this.body.stream().max((a, b) -> {
            if (a.getCol() > b.getCol()) {
                return 1;
            } else return -1;
        });
        return cell.get().getCol();
    }

    public void fall() {
        this.body.stream().forEach(cell -> {
            cell.setRow(cell.getRow() + 1);
        });
        this.center.setRow(center.getRow() + 1);
    }

    public void rightMove() {
        this.body.stream().forEach(cell -> {
            cell.setCol(cell.getCol() + 1);
        });
        this.center.setCol(center.getCol() + 1);
    }

    public void leftMove() {
        this.body.stream().forEach(cell -> {
            cell.setCol(cell.getCol() - 1);
        });
        this.center.setCol(center.getCol() - 1);
    }

    public void downMove() {
        this.body.stream().forEach(cell -> {
            cell.setRow(cell.getRow() + 1);
        });
        this.center.setRow(center.getRow() + 1);
    }
}

class Cell {
    int row;
    int col;

    public Cell() {
    }

    public Cell(int row, int col) {
        this.row = row;
        this.col = col;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getCol() {
        return col;
    }

    public void setCol(int col) {
        this.col = col;
    }

    public String print() {
        return "(" + row + ", " + col + ")";
    }
}

class IBlock extends Block {
    public static int LIE = 0;
    public static int STAND = 1;

    public IBlock(Cell... cells) {
        this.body = Arrays.asList(cells);
        this.center = new Cell(cells[1].getRow(), cells[1].getCol());
        this.curState = STAND;
    }


    private int curState;

}

class TBlock extends Block {
    private final static int DOWN = 0;
    private final static int LEFT = 1;
    private final static int UP = 2;
    private final static int RIGHT = 3;

    private int curState;

    public TBlock(Cell... cells) {
        this.body = Arrays.asList(cells);
        center = new Cell(body.get(1).getRow(), body.get(1).getCol());
        this.curState = UP;
    }

}


class LBlock extends Block {
    private final static int DOWN = 0;
    private final static int LEFT = 1;
    private final static int UP = 2;
    private final static int RIGHT = 3;

    private int curState;

    public LBlock(Cell... cells) {
        this.body = Arrays.asList(cells);
        center = new Cell(body.get(2).getRow(), body.get(2).getCol());
        this.curState = UP;
    }

}


class BlockFactory {

    public static Block createBlock(int type) {
        switch (type) {
            case 0:
                return new IBlock(
                        new Cell(0, 0),
                        new Cell(1, 0),
                        new Cell(2, 0),
                        new Cell(3, 0)
                );
            case 1:
                return new LBlock(
                        new Cell(3, 2),
                        new Cell(2, 2),
                        new Cell(2, 3),
                        new Cell(2, 4)
                );
            default:
                return new IBlock(
                        new Cell(1, 3),
                        new Cell(2, 3),
                        new Cell(3, 3),
                        new Cell(4, 3)
                );
        }

    }

}
